package com.model;

import com.model.math.Point2D;
import com.model.math.Point3D;
import com.model.math.Matrix3D;
import com.model.math.Vector3D;
import java.util.*;

public class ModelBoundingBoxCalculator {
    private static final boolean DEBUG = true;  // 调试开关
    
    private static void debugPrint(String message) {
        if (DEBUG) {
            System.out.println("[DEBUG] " + message);
        }
    }

    public static BoundingBox calculateOptimalBoundingBox(Point3D[] vertices) {
        if (vertices == null || vertices.length == 0) {
            return null;
        }

        debugPrint("Input vertices count: " + vertices.length);
        debugPrint("First vertex: " + vertices[0]);
        debugPrint("Last vertex: " + vertices[vertices.length - 1]);

        // 1. 计算协方差矩阵
        Matrix3D covMatrix = Matrix3D.computeCovarianceMatrix(vertices);
        debugPrint("Covariance Matrix:\n" + covMatrix);
        
        // 2. 计算主方向（特征向量）
        Vector3D[] principalDirections = covMatrix.computeEigenVectors();
        debugPrint("Principal Directions:");
        for (int i = 0; i < principalDirections.length; i++) {
            debugPrint("Axis " + i + ": " + principalDirections[i]);
        }
        
        // 3. 将顶点投影到主方向上
        Point3D[] projectedVertices = projectVertices(vertices, principalDirections);
        debugPrint("First projected vertex: " + projectedVertices[0]);
        debugPrint("Last projected vertex: " + projectedVertices[projectedVertices.length - 1]);
        
        // 4. 计算最小和最大点
        double minX = Double.MAX_VALUE;
        double minY = Double.MAX_VALUE;  // Y轴代表垂直方向
        double minZ = Double.MAX_VALUE;
        double maxX = -Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;  // Y轴代表垂直方向
        double maxZ = -Double.MAX_VALUE;
        
        for (Point3D vertex : projectedVertices) {
            minX = Math.min(minX, vertex.x);
            minY = Math.min(minY, vertex.y);  // 使用Y作为垂直高度
            minZ = Math.min(minZ, vertex.z);
            maxX = Math.max(maxX, vertex.x);
            maxY = Math.max(maxY, vertex.y);  // 使用Y作为垂直高度
            maxZ = Math.max(maxZ, vertex.z);
        }

        return new BoundingBox(
            new Point3D(minX, minY, minZ),
            new Point3D(maxX, maxY, maxZ)
        );
    }

    /**
     * 计算模型的边缘点
     */
    public static List<Point2D> calculateEdgePoints(Point3D[] vertices, ModelBoundingBox transformer) {
        debugPrint("=== 开始计算边缘点 ===");
        debugPrint("输入顶点数量: " + vertices.length);
        debugPrint("变换参数:");
        debugPrint(String.format("- 位置: 纬度=%.6f°, 经度=%.6f°", 
            transformer.getLatitude(), transformer.getLongitude()));
        debugPrint(String.format("- 旋转: X=%.2f°, Y=%.2f°, Z=%.2f°",
            Math.toDegrees(transformer.getRotationX()),
            Math.toDegrees(transformer.getRotationY()),
            Math.toDegrees(transformer.getRotationZ())));

        // 1. 应用变换（缩放和旋转）
        Point3D[] transformedVertices = transformer.applyTransformation(vertices);
        debugPrint("变换后顶点数量: " + transformedVertices.length);

        // 2. 投影到XY平面（从上方看）
        List<Point2D> projectedPoints = new ArrayList<>();
        for (Point3D vertex : transformedVertices) {
            projectedPoints.add(new Point2D(vertex.x, vertex.y)); // 使用x和y坐标
        }

        // 3. 使用凸包算法找出边缘点
        List<Point2D> convexHull = computeConvexHull(projectedPoints);
        debugPrint("凸包点数量: " + convexHull.size());

        // 4. 转换为经纬度坐标
        List<Point2D> edgePoints = new ArrayList<>();
        double earthRadius = 6378137.0; // 地球半径（米）
        double metersPerLatDegree = earthRadius * Math.PI / 180.0;
        double metersPerLonDegree = metersPerLatDegree * 
            Math.cos(Math.toRadians(transformer.getLatitude()));

        for (Point2D point : convexHull) {
            double lonOffset = point.x / metersPerLonDegree;
            double latOffset = point.y / metersPerLatDegree;
            edgePoints.add(new Point2D(
                transformer.getLongitude() + lonOffset,
                transformer.getLatitude() + latOffset
            ));
        }

        // 确保闭合
        if (!edgePoints.isEmpty()) {
            edgePoints.add(edgePoints.get(0));
        }

        debugPrint("最终边缘点数量: " + edgePoints.size());
        if (!edgePoints.isEmpty()) {
            debugPrint("第一个边缘点: " + edgePoints.get(0));
            debugPrint("最后一个边缘点: " + edgePoints.get(edgePoints.size() - 1));
        }

        return edgePoints;
    }

    private static List<Point2D> computeConvexHull(List<Point2D> points) {
        if (points.size() < 3) return points;

        // Sort points by x, then by y
        points.sort((p1, p2) -> {
            if (p1.x != p2.x) return Double.compare(p1.x, p2.x);
            return Double.compare(p1.y, p2.y);
        });

        List<Point2D> hull = new ArrayList<>();

        // Build lower hull
        for (Point2D p : points) {
            while (hull.size() >= 2 && cross(hull.get(hull.size() - 2), hull.get(hull.size() - 1), p) <= 0) {
                hull.remove(hull.size() - 1);
            }
            hull.add(p);
        }

        // Build upper hull
        int t = hull.size() + 1;
        for (int i = points.size() - 1; i >= 0; i--) {
            Point2D p = points.get(i);
            while (hull.size() >= t && cross(hull.get(hull.size() - 2), hull.get(hull.size() - 1), p) <= 0) {
                hull.remove(hull.size() - 1);
            }
            hull.add(p);
        }

        hull.remove(hull.size() - 1); // Remove the last point because it's the same as the first one

        return hull;
    }

    private static double cross(Point2D o, Point2D a, Point2D b) {
        return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
    }

    private static BoundingBox calculateBoundingBox(Point3D[] vertices) {
        double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE, minZ = Double.MAX_VALUE;
        double maxX = -Double.MAX_VALUE, maxY = -Double.MAX_VALUE, maxZ = -Double.MAX_VALUE;
        
        for (Point3D v : vertices) {
            minX = Math.min(minX, v.x);
            minY = Math.min(minY, v.y);
            minZ = Math.min(minZ, v.z);
            maxX = Math.max(maxX, v.x);
            maxY = Math.max(maxY, v.y);
            maxZ = Math.max(maxZ, v.z);
        }
        
        return new BoundingBox(
            new Point3D(minX, minY, minZ),
            new Point3D(maxX, maxY, maxZ)
        );
    }

    private static Point3D[] projectVertices(Point3D[] vertices, Vector3D[] basis) {
        debugPrint("\n=== 开始顶点投影 ===");
        debugPrint("基向量信息:");
        debugPrint("X轴基向量: " + basis[0]);  // 应该对应水平方向
        debugPrint("Y轴基向量: " + basis[1]);  // 应该对应垂直方向
        debugPrint("Z轴基向量: " + basis[2]);  // 应该对应深度方向
        
        Point3D[] projected = new Point3D[vertices.length];
        Point3D center = Point3D.computeCenter(vertices);
        debugPrint("Projection center: " + center);
        
        // 修改：确保Y轴朝上（对应Cesium中的垂直方向）
        Vector3D upVector = new Vector3D(0, 1, 0);
        
        // 找到最接近上方向的基向量
        int upIndex = 0;
        double maxUpDot = Math.abs(basis[0].dot(upVector));
        for (int i = 1; i < 3; i++) {
            double dot = Math.abs(basis[i].dot(upVector));
            if (dot > maxUpDot) {
                maxUpDot = dot;
                upIndex = i;
            }
        }
        
        // 如果Y轴不是最接近上方向的，交换基向量
        if (upIndex != 1) {
            Vector3D temp = basis[1];
            basis[1] = basis[upIndex];
            basis[upIndex] = temp;
        }
        
        // 确保Y轴方向朝上
        if (basis[1].dot(upVector) < 0) {
            basis[1] = basis[1].scale(-1);
            // 保持右手坐标系
            basis[2] = basis[2].scale(-1);
        }

        // 进行投影
        for (int i = 0; i < vertices.length; i++) {
            Point3D p = vertices[i];
            Vector3D v = new Vector3D(p.x - center.x, p.y - center.y, p.z - center.z);
            
            // 计算在新基下的坐标
            double x = v.dot(basis[0]);
            double y = v.dot(basis[1]);  // Y轴现在对应垂直方向
            double z = v.dot(basis[2]);
            
            projected[i] = new Point3D(x, y, z);
        }

        debugPrint("\n投影结果验证:");
        debugPrint("第一个顶点投影前: " + vertices[0]);
        debugPrint("第一个顶点投影后: " + projected[0]);
        debugPrint("验证垂直方向(Y轴)是否正确对齐");
        
        return projected;
    }

    /**
     * 生成GeoJSON格式的字符串
     */
    public static String generateGeoJSON(List<Point2D> points) {
        StringBuilder json = new StringBuilder();
        json.append("{\n");
        json.append("  \"type\": \"FeatureCollection\",\n");
        json.append("  \"features\": [\n");
        json.append("    {\n");
        json.append("      \"type\": \"Feature\",\n");
        json.append("      \"properties\": {},\n");
        json.append("      \"geometry\": {\n");
        json.append("        \"type\": \"Polygon\",\n");
        json.append("        \"coordinates\": [\n");
        json.append("          [\n");

        // 添加所有点
        for (Point2D point : points) {
            json.append(String.format("            [%.7f, %.7f],\n",
                point.x, point.y));
        }

        // 闭合多边形（添加第一个点）
        if (!points.isEmpty()) {
            Point2D firstPoint = points.get(0);
            json.append(String.format("            [%.7f, %.7f]\n",
                firstPoint.x, firstPoint.y));
        }

        json.append("          ]\n");
        json.append("        ]\n");
        json.append("      }\n");
        json.append("    }\n");
        json.append("  ]\n");
        json.append("}");

        return json.toString();
    }
}